package com.xkw.autocode.util;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 对象的属性复制
 * @author xiangkaiwei
 */
public class ObjectCopyUtils {

	private static final Logger logger = LoggerFactory.getLogger(ObjectCopyUtils.class.getName());
	
	private ObjectCopyUtils() {
		
	}
	
	@SuppressWarnings("rawtypes")
	public static List copyPropertiesList(List fromList, Class<?> toClass) throws ReflectiveOperationException {
		List<Object> list = new ArrayList<>();
		if(fromList == null || fromList.isEmpty()){
			return list;
		}
		for(Object o : fromList){
			list.add(copyWithOutProperties(o, toClass, null, false));
		}
		
		return list;
	}
	
	/**
	 * 对象属性值复制
	 * 
	 * @param from
	 * @param to
	 * @param isCopyEmpty
	 * @throws Exception
	 */
	public static Object copyProperties(Object from, Class<?> toClass) throws ReflectiveOperationException {
		Set<String> withOutProperties = new HashSet<>();
		withOutProperties.add("version");
		
		return copyWithOutProperties(from, toClass, withOutProperties, false);
	}
	
	/**
	 * 对象属性值复制
	 * 
	 * @param from
	 * @param to
	 * @param isCopyEmpty
	 * @throws Exception
	 */
	public static Object copyProperties(Object from, Class<?> toClass,boolean isCopyEmpty) throws ReflectiveOperationException {
		return copyWithOutProperties(from, toClass, null, isCopyEmpty);
	}
	
	public static Object copyWithOutProperties(Object from, Class<?> toClass,Set<String> withOutProperties) throws ReflectiveOperationException {
		return copyWithOutProperties(from, toClass, withOutProperties, false);
	}
	
	/**
	 * 对象属性值复制
	 * @param from
	 * @param toClass
	 * @param withOutProperties 排除哪些属性不复制
	 * @return
	 * @throws Exception
	 */
	public static Object copyWithOutProperties(Object from, Class<?> toClass,Set<String> withOutProperties,boolean isCopyEmpty) throws ReflectiveOperationException {
		if(from == null){
			return null;
		}

		//构造返回的对象
		Object to = toClass.newInstance();
		
		//获取对象的方法
		Method[] fromMethods = getMethods(from.getClass());
		Method[] toMethods = getMethods(toClass);
		
		Method fromMethod = null; 
		Method toMethod = null;
		
		String fromMethodName = null;
		String toMethodName = null;
		
		//循环设置对象
		for (int i = 0; i < fromMethods.length; i++) {
			fromMethod = fromMethods[i];
			fromMethodName = fromMethod.getName();
			
			//判断源对象是否有 get 方法
			if (!fromMethodName.startsWith("get")){
				continue;
			}
			
			String fieldName = fromMethodName.substring(3);
			
			//set方法
			toMethodName = "set" + fieldName;
			
			fieldName = fieldName.substring(0,1).toLowerCase() + fieldName.substring(1);

			//排除不需要复制的属性
			if(withOutProperties != null && withOutProperties.contains(fieldName)){
				continue;
			}
			
			//判断目标对象是否有set方法
			toMethod = findMethodByName(toMethods, toMethodName);
			if (toMethod == null){
				continue;
			}
			
			//获取原对象中的值
			Object value = fromMethod.invoke(from);
			
			//判断值是否为空，为空则跳过
			if (value == null){
				continue;
			}
			
			if (!isCopyEmpty && value instanceof String && String.valueOf(value).length() == 0) {
				continue;
			}
			
			// 集合类判空处理
			if (value instanceof Collection) {
				Collection<?> newValue = (Collection<?>) value;
				if (newValue == null ||newValue.isEmpty()) {
					continue;
				}
			}
			
			//判断类型是否一致
			Class<?>[] toParams = toMethod.getParameterTypes();
			if(toParams != null && toParams.length > 0) {
				Class<?> toParamType = toParams[0];
				
				String toType = toParamType.getTypeName();
				String fromType = fromMethod.getReturnType().getTypeName();
				
				if (!toType.equals(fromType)) {
					String toName = toType.substring(toType.lastIndexOf('.') + 1);
					String fromName = fromType.substring(fromType.lastIndexOf('.') + 1);
					if((fromName + "Vo").equals(toName) || (toName + "Vo").equals(fromName)) {
						try {
							value = copyWithOutProperties(value, Class.forName(toType), withOutProperties);
						} catch (Exception e) {
							logger.error("",e);
						}
					}else{
						continue;
					}
				}
			}
			
			//设置目标对象的属性值
			try {
				toMethod.invoke(to, value);
			}  catch (Exception e) {
				logger.error("---------------- " + fieldName + " fieldName exception",e);
				
			}
		}

		return to;
	}
	
	/**
	 * 对象属性值复制
	 * @param from
	 * @param toClass
	 * @param withOutProperties 排除哪些属性不复制
	 * @return
	 * @throws ReflectiveOperationException 
	 * @throws InstantiationException 
	 * @throws Exception
	 */
	public static Object copyMapProperties(Map<String,Object> map, Class<?> toClass,Set<String> withOutProperties) throws ReflectiveOperationException {
		if(map == null){
			return null;
		}

		//构造返回的对象
		Object to = toClass.newInstance();
		
		//获取对象的方法
		Method[] toMethods = getMethods(toClass);
		
		Method toMethod = null;
		String toMethodName = null;
		
		Set<String> removeKey = new HashSet<>();
		
		//循环设置对象
		for (Map.Entry<String, Object> entry : map.entrySet()) {
			String key = entry.getKey();
			Object value = map.get(key);
			if(value == null){
				continue;
			}
			
			//排除不需要复制的属性
			if(withOutProperties != null && withOutProperties.contains(key)){
				continue;
			}
			
			//set方法
			toMethodName = "set" + key.substring(0,1).toUpperCase() + key.substring(1);
			
			//判断目标对象是否有set方法
			toMethod = findMethodByName(toMethods, toMethodName);
			if (toMethod == null){
				continue;
			}
			
			//设置目标对象的属性值
			toMethod.invoke(to, value);
			
			//移除设置过的属性
			removeKey.add(key);
		}
		if(!removeKey.isEmpty()){
			for(String key : removeKey){
				map.remove(key);
			}
		}

		return to;
	}
	
	/**
	 * 获取类的所有方法
	 */
	private static Method[] getMethods(Class<?> c){
		
		//当前类的所有方法
		Method[] methods = c.getDeclaredMethods();
		
		Class<?> superClass = c.getSuperclass();
		if(superClass != null){
			Method[] superMethods = superClass.getDeclaredMethods();
			if(superMethods != null && superMethods.length > 0){
				return ArrayUtils.addAll(superMethods, methods);
			}
		}
		return methods;
	}
	
	/**
	 * 从方法数组中获取指定名称的方法
	 */
	private static Method findMethodByName(Method[] methods, String name) {
		for (int j = 0; j < methods.length; j++) {
			if (methods[j].getName().equals(name)) {
				return methods[j];
			}
		}
		return null;
	}
	
}